/*
 *  Copyright (c) 2023-2025 苏州棱镜七彩信息科技有限公司 (Suzhou PrismAurora Information Technology Co.,Ltd)
 *  sbom-generate is licensed under Mulan PSL v2.
 *  You can use this software according to the terms and conditions of the Mulan PSL v2.
 *  You may obtain a copy of Mulan PSL v2 at:
 *           http://license.coscl.org.cn/MulanPSL2
 *  THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 *  See the Mulan PSL v2 for more details.
 * */
package com.ljqc.impl.sbom;

import com.ljqc.abs.sbom.SBomGenerate;
import com.ljqc.entity.CycloneDXDocument;
import com.ljqc.entity.DocMetaInfo;
import com.ljqc.entity.PackageInfo;
import com.ljqc.utils.FileUtils;

import java.io.File;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;

/**
 * @ClassName CycloneDXGenerate
 * @Description CycloneDX JSON格式SBOM生成器
 * @Date 2025/3/14 13:46
 * @Version V1.0
 **/
public class CycloneDXGenerate implements SBomGenerate<CycloneDXDocument, PackageInfo> {
    private static final Logger LOGGER = Logger.getLogger(CycloneDXGenerate.class.getName());
    private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'");

    @Override
    public CycloneDXDocument generateByInfoList(List<PackageInfo> infoList, DocMetaInfo docMetaInfo) {
        LOGGER.log(Level.INFO, "开始生成CycloneDX文档，包信息数量：{0}", infoList.size());
        CycloneDXDocument cycloneDXDocument = new CycloneDXDocument();
        
        // 设置序列号
        String serialNumber = "urn:uuid:" + UUID.randomUUID();
        cycloneDXDocument.setSerialNumber(serialNumber);
        LOGGER.log(Level.FINE, "设置文档序列号：{0}", serialNumber);
        
        // 设置时间戳
        String timestamp = LocalDateTime.now(ZoneOffset.UTC).format(DATE_TIME_FORMATTER);
        cycloneDXDocument.setTimestamp(timestamp);
        LOGGER.log(Level.FINE, "设置文档时间戳：{0}", timestamp);
        
        // 转换包信息为组件
        LOGGER.log(Level.FINE, "开始转换包信息为CycloneDX组件");
        List<CycloneDXDocument.Component> components = infoList.stream()
                .map(pkg -> {
                    LOGGER.log(Level.FINE, "处理包：{0}", pkg.getName());
                    CycloneDXDocument.Component component = new CycloneDXDocument.Component();
                    
                    // 设置id，参考SPDXGenerate的实现
                    String pkgUuid = UUID.randomUUID().toString().substring(0, 8); /*截取8位做为标识符*/
                    String filePath = pkg.getFileName();
                    String fileMd5 = FileUtils.calculateMD5(new File(filePath)).substring(0, 8);  /*截取8位做为标识符*/
                    String componentType = pkg.getType() != null ? pkg.getType() : "unknown";
                    String id = String.format("BOM-Ref-PACKAGE-%s-%s-%s-%s-%s",
                            pkgUuid,
                            fileMd5,
                            componentType,
                            pkg.getName(),
                            pkg.getVersion());
                    component.setId(id);
                    LOGGER.log(Level.FINE, "生成组件ID：{0}", id);
                    
                    component.setName(pkg.getName());
                    component.setVersion(pkg.getVersion());
                    
                    // 设置externalRef，参考SPDXGenerate的实现
                    String externalRef = String.format("%s/%s@%s", 
                            componentType, 
                            pkg.getName().replace(":", "/"), 
                            pkg.getVersion());
                    component.setExternalRef(externalRef);
                    LOGGER.log(Level.FINE, "设置外部引用：{0}", externalRef);
                    
                    // 设置许可证
                    String license = pkg.getLicense() != null ? pkg.getLicense() : "NOASSERTION";
                    component.setLicenseConcluded(license);
                    LOGGER.log(Level.FINE, "设置许可证：{0}", license);
                    
                    return component;
                })
                .collect(Collectors.collectingAndThen(
                    Collectors.toMap(
                        comp -> comp.getName() + ":" + comp.getVersion(),
                        comp -> comp,
                        (existing, replacement) -> {
                            LOGGER.log(Level.FINE, "发现重复组件：{0}:{1}，保留第一个", 
                                    new Object[]{existing.getName(), existing.getVersion()});
                            return existing;
                        }
                    ),
                    map -> new ArrayList<>(map.values())
                ));
        cycloneDXDocument.setPackages(components);
        LOGGER.log(Level.FINE, "组件转换完成，去重后共 {0} 个组件", components.size());

        // 创建组件ID映射，用于快速查找
        LOGGER.log(Level.FINE, "创建组件ID映射");
        Map<String, CycloneDXDocument.Component> componentMap = components.stream()
                .collect(Collectors.toMap(
                        comp -> comp.getName() + ":" + comp.getVersion(),
                        comp -> comp,
                        (existing, replacement) -> existing
                ));

        // 处理依赖关系，按父组件分组
        LOGGER.log(Level.FINE, "开始处理依赖关系");
        Map<String, List<String>> parentChildMap = infoList.stream()
                .filter(pkg -> pkg.getParentName() != null && !pkg.getParentName().isEmpty())
                .collect(Collectors.groupingBy(
                        pkg -> pkg.getParentName() + ":" + pkg.getParentVersion(),
                        Collectors.mapping(
                                pkg -> pkg.getName() + ":" + pkg.getVersion(),
                                Collectors.toList()
                        )
                ));
        LOGGER.log(Level.FINE, "依赖关系分组完成，共 {0} 个父组件", parentChildMap.size());

        // 转换为依赖关系列表
        List<CycloneDXDocument.Dependency> dependencies = parentChildMap.entrySet().stream()
                .map(entry -> {
                    LOGGER.log(Level.FINE, "处理父组件 {0} 的依赖", entry.getKey());
                    CycloneDXDocument.Dependency dependency = new CycloneDXDocument.Dependency();
                    
                    // 获取父组件
                    CycloneDXDocument.Component parentComponent = componentMap.get(entry.getKey());
                    if (parentComponent == null) {
                        LOGGER.log(Level.FINE, "未找到父组件");
                        return null;
                    }
                    
                    // 获取所有子组件ID并去重
                    List<String> childIds = entry.getValue().stream()
                            .map(componentMap::get)
                            .filter(Objects::nonNull)
                            .map(CycloneDXDocument.Component::getId)
                            .distinct()  // 去重
                            .collect(Collectors.toList());
                    
                    if (!childIds.isEmpty()) {
                        dependency.setParentId(parentComponent.getId());
                        dependency.setNames(childIds);
                        LOGGER.log(Level.FINE, "添加依赖关系：父组件={0}, 子组件数量={1}", 
                                new Object[]{parentComponent.getId(), childIds.size()});
                        return dependency;
                    }
                    
                    LOGGER.log(Level.FINE, "父组件没有有效的子组件");
                    return null;
                })
                .filter(Objects::nonNull)
                .collect(Collectors.toList());

        cycloneDXDocument.setDependencies(dependencies);
        LOGGER.log(Level.INFO, "CycloneDX文档生成完成，组件数量：{0}，依赖关系数量：{1}", 
                new Object[]{components.size(), dependencies.size()});

        return cycloneDXDocument;
    }
}
